package com.baixiaowen.xiaoaointerview.JVM原理篇;

import javassist.*;
import org.junit.Test;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 自定义ClassLoader
 *
 * @author Baixiaowen
 */
public class MyClassLoader {

    /**
     * 生成一个类
     *
     * @return
     */
    private static byte[] genClass() throws CannotCompileException, IOException {
        // javassist Java助手
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.getOrNull("greetings.Go");
        if (ctClass != null) {
            // 解冻
            ctClass.defrost();
        }

        /** 在做
         * package greetings
         *
         * public class Go{
         *      public void greetings() {}
         * }
         */
        ctClass = pool.makeClass("greetings.Go");
        CtMethod method = new CtMethod(CtClass.voidType, "greetings", new CtClass[]{}, ctClass);
        method.setModifiers(Modifier.PUBLIC);
        method.setBody("{ System.out.println(\"Hi Greetings!!\"); }");
        ctClass.addMethod(method);
        return ctClass.toBytecode();
    }

    /**
     * 加载二进制的ClassLoader
     */
    class BinLoader extends ClassLoader {
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            if(name == "greetings.Go") {
                byte[] bytes = new byte[0];
                try {
                    bytes = genClass();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return defineClass("greetings.Go", bytes, 0, bytes.length);
            }
            return super.findClass(name);
        }
    }

    class NetLoader extends ClassLoader {
        byte[] bytes = new byte[1024];

        public NetLoader() throws IOException {
            this.connect();
        }

        private void connect() throws IOException {
            try(Socket socket = new Socket("localhost", 8000)) {
                int read = socket.getInputStream().read(bytes);
            }
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            if(name == "greetings.Go") {
                return defineClass("greetings.Go", bytes, 0, bytes.length);
            }
            return super.findClass(name);
        }
    }

    @Test
    public void test_gen() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ClassLoader loader = new BinLoader();
        Class<?> goClazz = loader.loadClass("greetings.Go");
        Object go = goClazz.getConstructor().newInstance();
        go.getClass().getMethod("greetings").invoke(go);
    }



    @Test
    public void server() throws IOException, CannotCompileException, NotFoundException {
        ServerSocket serverSocket = new ServerSocket(8000);
        byte[] bytes = genClass();
        while(true) {
            try(Socket clientSocket = serverSocket.accept()) {
                System.out.println("receve request...");
                OutputStream out = clientSocket.getOutputStream();
                out.write(bytes);
                out.flush();
            }
        }
    }

    @Test
    public void test_net() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        NetLoader loader = new NetLoader();
        Class<?> goclazz = loader.loadClass("greetings.Go");
        Object go = goclazz.getConstructor().newInstance();
        go.getClass().getMethod("greetings").invoke(go);
    }

}
